home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / mewin10s.zip / MSWMENU.C < prev    next >
C/C++ Source or Header  |  1992-03-15  |  41KB  |  1,337 lines

  1. /* The routines in this file provide menu-related functions under the
  2.    Microsoft Windows environment on an IBM-PC or compatible computer.
  3.  
  4.    This module also contains the code for the About and Modes dialog
  5.    boxes.
  6.  
  7.    Must be compiled with Borland C++ 2.0.
  8.  
  9.    It should not be compiled if the WINDOW_MSWIN symbol is not set */
  10.  
  11. #include    "estruct.h"
  12. #include    "elang.h"
  13. #include    <stdio.h>
  14. #include    "eproto.h"
  15. #include    "edef.h"
  16.  
  17. #include    "mswin.h"
  18. #include    "mswmenu.h"
  19. #include    "mswhelp.h"
  20.  
  21. #define MAXMENUTITLE 50
  22.  
  23. /* codes for the MenuType parameter from AddMenuEntry () */
  24. #define MT_DUMMY    1
  25. #define MT_MENUBAR  2
  26.  
  27. typedef struct   {
  28.     WORD    m_word;
  29.     ETYPE EPOINTER m_ptr;
  30. } MENUTAB;
  31.  
  32. #define MB_FNC  0x4000
  33. #define MB_BUF  0x8000  /* static IDs should not look like they carry
  34.                this flag */
  35.  
  36. #define MAXDYNMENU  256
  37.  
  38. static MENUTAB MenuDynBind [MAXDYNMENU] = {0, NULL};
  39.  
  40. static MENUTAB MenuStaticBind [] = {
  41.     {IDM_NULLPROC,      nullproc},
  42.     {IDM_FILEFIND,      filefind},
  43.     {IDM_VIEWFILE,      viewfile},
  44.     {IDM_INSFILE,    insfile},
  45.     {IDM_FILEREAD,    fileread},
  46.     {IDM_FILENAME,    filename},
  47.     {IDM_FILESAVE,    filesave},
  48.     {IDM_FILEWRITE,    filewrite},
  49.     {IDM_FILEAPP,    fileapp},
  50.     {IDM_SETEKEY,    setekey},
  51.     {IDM_NEXTBUFFER,    nextbuffer},
  52.     {IDM_USEBUFFER,    usebuffer},
  53.     {IDM_UNMARK,    unmark},
  54.     {IDM_NAMEBUFFER,    namebuffer},
  55.     {IDM_KILLBUFFER,    killbuffer},
  56.     {IDM_NARROW,    narrow},
  57.     {IDM_WIDEN,        widen},
  58.     {IDM_LISTBUFFERS,    listbuffers},
  59.     {IDM_SPLITWIND,    splitwind},
  60.     {IDM_DELWIND,    delwind},
  61.     {IDM_ONLYWIND,    onlywind},
  62.     {IDM_NEXTWIND,    nextwind},
  63.     {IDM_PREVWIND,    prevwind},
  64.     {IDM_MVUPWIND,    mvupwind},
  65.     {IDM_MVDNWIND,    mvdnwind},
  66.     {IDM_NEXTUP,    nextup},
  67.     {IDM_NEXTDOWN,    nextdown},
  68.     {IDM_ENLARGEWIND,    enlargewind},
  69.     {IDM_SHRINKWIND,    shrinkwind},
  70.     {IDM_RESIZE,    resize},
  71.     {IDM_QUICKEXIT,    quickexit},
  72.     {IDM_QUIT,        quit},
  73.         /* here just to compute the key binding: the actual IDM_QUIT
  74.        selection is processed as a special */
  75.     {IDM_CUTREGION,    cutregion},
  76.     {IDM_CLIPREGION,    clipregion},
  77.     {IDM_INSERTCLIP,    insertclip},
  78.     {IDM_SETMARK,    setmark},
  79.     {IDM_REMMARK,    remmark},
  80.     {IDM_SWAPMARK,    swapmark},
  81.     {IDM_YANK,        yank},
  82.     {IDM_KILLREGION,    killregion},
  83.     {IDM_COPYREGION,    copyregion},
  84.     {IDM_UPPERREGION,    upperregion},
  85.     {IDM_LOWERREGION,    lowerregion},
  86.     {IDM_ENTAB,        entab},
  87.     {IDM_DETAB,        detab},
  88.     {IDM_TRIM,        trim},
  89.     {IDM_INDENT_REGION,    indent_region},
  90.     {IDM_UNDENT_REGION,    undent_region},
  91.     {IDM_WORDCOUNT,    wordcount},
  92.     {IDM_FILLPARA,    fillpara},
  93.     {IDM_KILLPARA,    killpara},
  94.     {IDM_KILLTEXT,    killtext},
  95.     {IDM_OPENLINE,    openline},
  96.     {IDM_DELFWORD,    delfword},
  97.     {IDM_DELBWORD,    delbword},
  98.     {IDM_CAPWORD,    capword},
  99.     {IDM_LOWERWORD,    lowerword},
  100.     {IDM_UPPERWORD,    upperword},
  101.     {IDM_DEBLANK,    deblank},
  102.     {IDM_TWIDDLE,    twiddle},
  103.     {IDM_TAB,        tab},
  104.     {IDM_QUOTE,        quote},
  105.     {IDM_SETFILLCOL,    setfillcol},
  106.     {IDM_FORWSEARCH,    forwsearch},
  107.     {IDM_BACKSEARCH,    backsearch},
  108.     {IDM_FORWHUNT,    forwhunt},
  109.     {IDM_BACKHUNT,    backhunt},
  110.     {IDM_FISEARCH,    fisearch},
  111.     {IDM_RISEARCH,    risearch},
  112.     {IDM_SREPLACE,    sreplace},
  113.     {IDM_QREPLACE,    qreplace},
  114.     {IDM_GOTOMARK,    gotomark},
  115.     {IDM_GOTOLINE,    gotoline},
  116.     {IDM_GETFENCE,    getfence},
  117.     {IDM_GOTOBOB,    gotobob},
  118.     {IDM_GOTOEOB,    gotoeob},
  119.     {IDM_FORWPAGE,    forwpage},
  120.     {IDM_BACKPAGE,    backpage},
  121.     {IDM_GOTOEOP,    gotoeop},
  122.     {IDM_GOTOBOP,    gotobop},
  123.     {IDM_FORWLINE,    forwline},
  124.     {IDM_BACKLINE,    backline},
  125.     {IDM_GOTOBOL,    gotobol},
  126.     {IDM_GOTOEOL,    gotoeol},
  127.     {IDM_FORWWORD,    forwword},
  128.     {IDM_BACKWORD,    backword},
  129.     {IDM_ENDWORD,    endword},
  130.     {IDM_EXECPRG,    execprg},
  131.     {IDM_SPAWNCLI,    spawncli},
  132.     {IDM_SPAWN,        spawn},
  133.     {IDM_PIPECMD,    pipecmd},
  134.     {IDM_FILTER,    filter},
  135.     {IDM_CTLXE,        ctlxe},
  136.     {IDM_CTLXLP,    ctlxlp},
  137.     {IDM_CTLXRP,    ctlxrp},
  138.     {IDM_NAMEDCMD,    namedcmd},
  139.     {IDM_EXECCMD,    execcmd},
  140.     {IDM_EXECPROC,    execproc},
  141.     {IDM_EXECBUF,    execbuf},
  142.     {IDM_EXECFILE,    execfile},
  143.     {IDM_BINDTOKEY,    bindtokey},
  144.     {IDM_MACROTOKEY,    macrotokey},
  145.     {IDM_BINDTOMENU,    bindtomenu},
  146.     {IDM_MACROTOMENU,    macrotomenu},
  147.     {IDM_UNBINDKEY,    unbindkey},
  148.     {IDM_UNBINDMENU,    unbindmenu},
  149.     {IDM_DESKEY,    deskey},
  150.     {IDM_SETVAR,    setvar},
  151.     {IDM_DISPVAR,    dispvar},
  152.     {IDM_DESVARS,    desvars},
  153.     {IDM_SHOWCPOS,    showcpos},
  154.     {IDM_CTRLG,        ctrlg},
  155.     {IDM_FIND_SCREEN,    find_screen},
  156.     {IDM_RENAMESCREEN,    renamescreen},
  157.     {IDM_NEWSIZE,    newsize},
  158.     {IDM_NEWWIDTH,    newwidth},
  159.     {IDM_DESBIND,    desbind},
  160.     {IDM_DESFUNC,    desfunc},
  161.     {IDM_APRO,        apro},
  162.     {0,                 NULL}
  163. };
  164.  
  165. #define MAXMENUPARENT   7
  166.  
  167. typedef struct {    /* current menu description */
  168.     int     cm_pos;     /* entry position */
  169.     int     cm_x;       /* index of the last parent handle, -1 if entry
  170.                located in main menu bar */
  171.     HMENU   cm_parent[MAXMENUPARENT];
  172.                         /* parent handle, main popup menu at index 0 */
  173. } CURMENU;
  174.  
  175. static CURMENU  CurrentMenu = {-1, -1, {NULL}};
  176.  
  177. static unsigned int ctrlx_key = 0;
  178. static unsigned int meta_key = 0;       /* for GetKeyText */
  179.  
  180. /* execmenu:    execute a function bound to a menu */
  181. /* ========                                        */
  182.  
  183. /* This function is called by the edit loop in main.c, when a MENU
  184.    extended character is detected. */
  185.  
  186. PASCAL execmenu (int f, int n)
  187. /* f, n: arguments to target function */
  188. {
  189.     register WORD    ID;
  190.     register MENUTAB *MTp;
  191.  
  192.     ID = (xpos << 8) + ypos;    /* getkey sees MENU sequences as MOUS */
  193.     if (ID >= IDM_DYNAMIC) {
  194.         MTp = &MenuDynBind[ID - IDM_DYNAMIC];
  195.         if (MTp->m_word & MB_FNC) return (*(MTp->m_ptr.fp)) (f, n);
  196.         else if (MTp->m_word & MB_BUF) {
  197.             BOOL    status;
  198.  
  199.             if (f == FALSE) n = 1;
  200.             while (n--) {
  201.                 status = dobuf (MTp->m_ptr.buf);
  202.                 if (status != TRUE) return status;
  203.             }
  204.         }
  205.         else return FAILED;
  206.         return TRUE;
  207.     }
  208.     else for (MTp = &MenuStaticBind[0]; MTp->m_word != 0; MTp++) {
  209.         if (ID == MTp->m_word) return (*(MTp->m_ptr.fp)) (f, n);
  210.     }
  211.     return FAILED;      /* not found !!! */
  212. } /* execmenu */
  213.  
  214. /* GenerateMenuSeq: send a menu sequence into the input stream */
  215. /* ===============                                             */
  216.  
  217. static void near pascal GenerateMenuSeq (WORD ID)
  218. {
  219.     if (!in_room (5)) return;
  220.     in_put (0);             /* escape indicator */
  221.     in_put (MENU >> 8);     /* MENU prefix */
  222.     in_put (ID >> 8);       /* menu ID high byte (--> xpos) */
  223.     in_put (ID & 0xFF);     /* menu ID low byte (--> ypos) */
  224.     in_put ('m');           /* dummy event */
  225. } /* GenerateMenuSeq */
  226.  
  227. /* AboutDlgProc:  About box dialog function */
  228. /* ============                             */
  229. int EXPORT far pascal  AboutDlgProc (HWND hDlg, WORD wMsg, WORD wParam,
  230.                                      DWORD lParam)
  231. {
  232.     char    s [50];
  233.     static RECT FullBox;
  234.     
  235.     switch (wMsg) {
  236.         
  237.     case WM_INITDIALOG:
  238.         strcpy (s, PROGNAME);
  239.         strcat (s, " ");
  240.         strcat (s, VERSION);
  241.         SetDlgItemText (hDlg, ID_PROGVER, s);
  242.         {
  243.             RECT    Bar;
  244.  
  245.         GetWindowRect (hDlg, &FullBox);
  246.             GetWindowRect (GetDlgItem (hDlg, ID_ABOUTBAR), &Bar);
  247.             MoveWindow (hDlg, FullBox.left, FullBox.top,
  248.                         Bar.left + GetSystemMetrics (SM_CXDLGFRAME)
  249.                                  - FullBox.left,
  250.                         FullBox.bottom - FullBox.top,
  251.                         FALSE);     /* shrink dialog box, do not repaint */
  252.         }
  253.     return TRUE;
  254.     
  255.     case WM_COMMAND:
  256.     if (HIWORD(lParam) != BN_CLICKED) break;
  257.     if (wParam == 1) {
  258.         EndDialog (hDlg, 0);
  259.         return TRUE;
  260.     }
  261.     else if (wParam == ID_MOREABOUT) {
  262.         EnableWindow (LOWORD(lParam), FALSE);   /* disable "More >>" */
  263.         SetFocus (GetDlgItem (hDlg, 1));         /* make "OK" default */
  264.         MoveWindow (hDlg, FullBox.left, FullBox.top,
  265.                     FullBox.right - FullBox.left,
  266.                         FullBox.bottom - FullBox.top,
  267.                         TRUE);      /* enlarge dialog box and repaint */
  268.         return TRUE;
  269.     }
  270.     break;
  271.     default:
  272.     return FALSE;
  273.     }
  274.     return FALSE;
  275. } /* AboutDlgProc */
  276.  
  277. /* SetCheck:    puts a check mark in a check box */
  278. /* ========                                      */
  279.  
  280. void near pascal    SetCheck (HWND hDlg, int BoxID)
  281. {
  282.     SendMessage (GetDlgItem (hDlg, BoxID), BM_SETCHECK, 1, 0L);
  283. } /* SetCheck */
  284.  
  285. /* GetCheck:    TRUE is the check box is checked */
  286. /* ========                                      */
  287.  
  288. BOOL near pascal    GetCheck (HWND hDlg, int BoxID)
  289. {
  290.     return (SendMessage (GetDlgItem (hDlg, BoxID), BM_GETCHECK, 0, 0L) != 0);
  291. } /* GetCheck */
  292.  
  293. /* ModeDlgProc: Modes dialog box function */
  294. /* ===========                            */
  295.  
  296. /* must be invoked through DialogBoxParam, with LOWORD(dwInitParam) set
  297.    to TRUE for global modes and FALSE for current buffer modes */
  298.  
  299. int EXPORT far pascal  ModeDlgProc (HWND hDlg, WORD wMsg, WORD wParam,
  300.                                     DWORD lParam)
  301. {
  302.     char    s[40+NBUFN];
  303.     static int *modep;      /* mode flags pointer */
  304.     
  305.     switch (wMsg) {
  306.         
  307.     case WM_INITDIALOG:
  308.         if (LOWORD(lParam)) {
  309.         strcpy (s, TEXT331);    /* "Global modes" */
  310.         modep = &gmode;
  311.     }
  312.         else {
  313.         strcpy (s, TEXT332);    /* "Modes for buffer: " */
  314.         strcat (s, curbp->b_bname);
  315.         modep = &(curbp->b_mode);
  316.     }
  317.         SetWindowText (hDlg, s);
  318.         if (*modep & MDWRAP) SetCheck (hDlg, ID_WRAP);
  319.     if (*modep & MDCMOD) SetCheck (hDlg, ID_CMODE);
  320.         if (*modep & MDEXACT) SetCheck (hDlg, ID_EXACT);
  321.         if (*modep & MDVIEW) SetCheck (hDlg, ID_VIEW);
  322.         if (*modep & MDOVER) SetCheck (hDlg, ID_OVER);
  323.         if (*modep & MDREPL) SetCheck (hDlg, ID_REP);
  324.         if (*modep & MDMAGIC) SetCheck (hDlg, ID_MAGIC);
  325.         if (*modep & MDCRYPT) SetCheck (hDlg, ID_CRYPT);
  326.         if (*modep & MDASAVE) SetCheck (hDlg, ID_ASAVE);
  327.     return TRUE;
  328.     
  329.     case WM_COMMAND:
  330.     if (HIWORD(lParam) != BN_CLICKED) break;
  331.     switch (wParam) {
  332.         
  333.     case 1:        /* OK */
  334.         *modep = 0;
  335.         if (GetCheck (hDlg, ID_WRAP)) *modep |= MDWRAP;
  336.         if (GetCheck (hDlg, ID_CMODE)) *modep |= MDCMOD;
  337.         if (GetCheck (hDlg, ID_EXACT)) *modep |= MDEXACT;
  338.         if (GetCheck (hDlg, ID_VIEW)) *modep |= MDVIEW;
  339.         if (GetCheck (hDlg, ID_OVER)) *modep |= MDOVER;
  340.         if (GetCheck (hDlg, ID_REP)) *modep |= MDREPL;
  341.         if (GetCheck (hDlg, ID_MAGIC)) *modep |= MDMAGIC;
  342.         if (GetCheck (hDlg, ID_CRYPT)) *modep |= MDCRYPT;
  343.         if (GetCheck (hDlg, ID_ASAVE)) *modep |= MDASAVE;
  344.         EndDialog (hDlg, 0);
  345.         break;
  346.         
  347.     case 2:        /* Cancel */
  348.         EndDialog (hDlg, -1);
  349.         break;
  350.     
  351.     case ID_OVER:
  352.         /* if OVER set, clear the REP mode check-box */
  353.         if (GetCheck (hDlg, ID_OVER)) {
  354.         SendMessage (GetDlgItem (hDlg, ID_REP), BM_SETCHECK, 0, 0L);
  355.         }
  356.         break;
  357.  
  358.     case ID_REP:
  359.         /* if REP set, clear the OVER mode check-box */
  360.         if (GetCheck (hDlg, ID_REP)) {
  361.         SendMessage (GetDlgItem (hDlg, ID_OVER), BM_SETCHECK, 0, 0L);
  362.         }
  363.         break;
  364.  
  365.     }
  366.     break;
  367.  
  368.     default:
  369.     return FALSE;
  370.     }
  371.     return FALSE;
  372. } /* ModeDlgProc */
  373.  
  374. /* GetMenuEntryID:   give back ID of MenuItem or popup */
  375. /* ==============                                      */
  376.  
  377. /* if the entry is a menu item (not a popup), its ID is returned. If the
  378.    entry is a popup, the ID of the first entry in this popup is
  379.    returned, added to n*IDM_POPUP where n is the number of popup levels.
  380.  
  381.    If the entry is neither an item nor a popup, an item id of 0 is
  382.    returned */
  383.  
  384. WORD near pascal    GetMenuEntryID (HMENU hMenu, int Position)
  385. {
  386.     WORD    id;
  387.     HMENU   hSubMenu;
  388.     
  389.     if ((int)(id = GetMenuItemID (hMenu, Position)) == -1) {
  390.         if ((hSubMenu = GetSubMenu (hMenu, Position)) != NULL) {
  391.         id = IDM_POPUP + GetMenuEntryID (hSubMenu, 0);
  392.     }
  393.     else id = 0;
  394.     }
  395.     return id;
  396. } /* GetMenuEntryID */
  397.  
  398. /* FindKeyBinding:  scan the key binding table, return first match */
  399. /* ==============                                                  */
  400.  
  401. /* If there is no match, the returned value point to the BINDNUL entry
  402.    */
  403.  
  404. KEYTAB * near pascal FindKeyBinding (void *Func)
  405. {
  406.     register KEYTAB *KTp;
  407.  
  408.     for (KTp = &keytab[0]; KTp->k_type != BINDNUL; ++KTp) {
  409.     if (((KTp->k_type == BINDFNC) && (KTp->k_ptr.fp == Func)) ||
  410.         ((KTp->k_type == BINDBUF) && (KTp->k_ptr.buf == Func)))
  411.         if (!(KTp->k_code & MOUS)) break;   /* found it! (we skip
  412.                            mouse bindings) */
  413.     }
  414.     return KTp;
  415.     
  416. } /* FindKeyBinding */
  417.  
  418. /* GetKeyText:  translates a key code into a CUA-type description */
  419. /* ==========                                                     */
  420.  
  421. /* Returns the length (excluding the terminating NULL) of the text if it
  422.    fits in the supplied buffer, 0 otherwise (in which case the buffer
  423.    contents are unuseable). */
  424.  
  425. /* This function uses the ctrlx_key and meta_key static variables to
  426.    avoid scanning the key binding table too often. If these are NULL,
  427.    however, it looks them up. Therefore, these variables can be zeroed
  428.    if there is a doubt about their validity */
  429.  
  430. int near pascal GetKeyText (int Key, char *Text, int TextLength)
  431. {
  432.     int     i;      /* index in Text */
  433.     char    c;
  434.  
  435.     if (Key & (CTLX | META)) {
  436.         unsigned *prefix_key_ptr;
  437.         int     n;
  438.  
  439.         prefix_key_ptr = (Key & CTLX) ? &ctrlx_key : &meta_key;
  440.     if (*prefix_key_ptr == 0) {
  441.         KEYTAB  *KTp;
  442.  
  443.         KTp = FindKeyBinding ((Key & CTLX) ? cex : meta);
  444.         if (KTp->k_type == BINDNUL) return 0;
  445.         *prefix_key_ptr = KTp->k_code;
  446.     }
  447. #define NBSPACES    3   /* # of spaces after prefix */
  448.     i = GetKeyText (*prefix_key_ptr, Text, TextLength - NBSPACES);
  449.     if (i == 0)  return 0;
  450.     for (n = 0; n < NBSPACES; ++n) Text[i++] = ' ';
  451.     }
  452.     else {
  453.     i = 0;
  454.     }
  455.  
  456. #define APPENDTEXT(s)   {if (TextLength - i < sizeof(s)) return 0;\
  457.                          strcpy (&Text[i], s); i += sizeof(s) - 1;}
  458.  
  459.     c = (char)Key;
  460.     
  461.     if (Key & ALTD) APPENDTEXT(TEXT310) /* "Alt+" */
  462.     
  463.     if (Key & SHFT) APPENDTEXT(TEXT311) /* "Shift+" */
  464.     
  465.     if (Key & CTRL) {
  466.         switch (c) {
  467.     case 'H':
  468.         APPENDTEXT(TEXT312) /* "BkSp" */
  469.         goto all_done;
  470.     case 'I':
  471.         APPENDTEXT(TEXT313) /* "Tab" */
  472.         goto all_done;
  473.     case 'M':
  474.         APPENDTEXT(TEXT314) /* "Enter" */
  475.         goto all_done;
  476.     case '[':
  477.         APPENDTEXT(TEXT315)  /* "Esc" */
  478.         goto all_done;
  479.     default:
  480.         APPENDTEXT(TEXT316) /* "Ctrl+" */
  481.         break;
  482.     }
  483.     }
  484.     
  485.     if (Key & SPEC) {
  486.         switch (c) {
  487.     case '<':
  488.         APPENDTEXT(TEXT317) /* "Home" */
  489.         break;
  490.     case 'N':
  491.         APPENDTEXT(TEXT318) /* "DownArrow" */
  492.         break;
  493.     case 'P':
  494.         APPENDTEXT(TEXT319) /* "UpArrow" */
  495.         break;
  496.     case 'B':
  497.         APPENDTEXT(TEXT320) /* "LeftArrow" */
  498.         break;
  499.     case 'F':
  500.         APPENDTEXT(TEXT321) /* "RightArrow" */
  501.         break;
  502.     case '>':
  503.         APPENDTEXT(TEXT322) /* "End" */
  504.         break;
  505.     case 'Z':
  506.         APPENDTEXT(TEXT323) /* "PageUp" */
  507.         break;
  508.     case 'V':
  509.         APPENDTEXT(TEXT324) /* "PageDown" */
  510.         break;
  511.     case 'C':
  512.         APPENDTEXT(TEXT325) /* "Ins" */
  513.         break;
  514.     case 'D':        
  515.         APPENDTEXT(TEXT326) /* "Del" */
  516.         break;
  517.     case '0':
  518.         APPENDTEXT(TEXT327) /* "F10" */
  519.         break;
  520.     default:    /* single digit function key */
  521.         if ((c < '1') || (c > 9)) return 0; /* unknown! */
  522.         if (TextLength - i < 3) return 0;
  523.         Text[i++] = CHAR328;    /* 'F' */
  524.         Text[i++] = c;
  525.         break;
  526.         }
  527.     }
  528.     else if (c == ' ') APPENDTEXT(TEXT329)  /* "SpaceBar" */
  529.     else {
  530.         /* normal char */
  531.         if (TextLength - i < 2) return 0;
  532.         Text[i++] = c;
  533.     }
  534. all_done:    
  535.     Text[i] = '\0';
  536.     return i;
  537. } /* GetKeyText */
  538.  
  539. /* UpdateMenuItemText:  sets the key binding info in a menu item */
  540. /* ==================                                            */
  541.  
  542. void near pascal    UpdateMenuItemText (HMENU hMenu, int Position,
  543.                                         MENUTAB *MTp)
  544. {
  545.     KEYTAB  *KTp;
  546.     char    NewText[MAXMENUTITLE];
  547.     char    OldText[MAXMENUTITLE];
  548.     register int i;
  549.  
  550.     GetMenuString (hMenu, Position, OldText, MAXMENUTITLE, MF_BYPOSITION);
  551.     strcpy (NewText, OldText);
  552.     for (i = 0; (NewText[i] != '\0') && (NewText[i] != '\t'); ++i) ;
  553.         /* find the first tab char or the string's end */
  554.  
  555.     KTp = FindKeyBinding (MTp->m_word & MB_BUF ? (void*)MTp->m_ptr.buf :
  556.                          (void*)MTp->m_ptr.fp);
  557.     if (KTp->k_type != BINDNUL) {
  558.     NewText[i] = '\t';
  559.     if (!GetKeyText (KTp->k_code, &NewText[i+1], MAXMENUTITLE + 1 - i))
  560.         goto no_binding;    /* if out of room, no binding text */
  561.     }
  562.     else {
  563. no_binding:
  564.         /* let's erase the binding text */
  565.     NewText[i] = '\0';
  566.     }
  567.     if (strcmp (NewText, OldText) == 0) return;
  568.     ModifyMenu (hMenu, Position, MF_BYPOSITION | MF_STRING,
  569.                 GetMenuItemID (hMenu, Position), NewText); 
  570. } /* UpdateMenuItemText */
  571.  
  572. /* InitMenuPopup:   perform popup menu init before display (WM_INITMENUPOPUP) */
  573. /* =============                                                              */
  574.  
  575. /* here, we may gray menu entries that are invalid. We also try to
  576.    display a key binding after each menu item */
  577.  
  578. void far pascal InitMenuPopup (HMENU hMenu, DWORD lParam)
  579. {
  580.     int     Position;
  581.     int     ItemCount;
  582.     BOOL    Enable;
  583.     WORD    EntryID;
  584.     register MENUTAB *MTp; /* points the appropriate entry in MenuStaticBind */
  585.     MENUTAB *PrevMTp;      /* to control MenuStaticBind scanning */
  586.  
  587.     if (HIWORD(lParam)) return;     /* do not process the system menu */
  588.  
  589.     ctrlx_key = meta_key = 0;       /* these may have changed since the
  590.                        last call to GetKeyText */
  591.     
  592.     /*-Scan the menu's items */
  593.     ItemCount = GetMenuItemCount (hMenu);
  594.     PrevMTp = &MenuStaticBind[0];
  595.     for (Position =  0; Position < ItemCount; Position++) {
  596.         EntryID = GetMenuEntryID (hMenu, Position);
  597.  
  598.         /*-lookup the item in the menu binding table */
  599.         /* since we assume the items' order follows the binding table,
  600.        we try to optimize the lookup by starting from the last
  601.        found entry */
  602.         if (EntryID >= IDM_POPUP) {
  603.         /* for popup menus MTp yields the nullproc entry */
  604.             MTp = &MenuStaticBind[0];
  605.         }
  606.         else if (EntryID >= IDM_DYNAMIC) {
  607.         /* for dynamic items, MTp is directly set to the proper
  608.            entry in the MenuDynBind */
  609.         MTp = &MenuDynBind[EntryID - IDM_DYNAMIC];
  610.     }
  611.         else {
  612.             MTp = PrevMTp + 1;
  613.         for (;;) {
  614.             if (MTp->m_word == EntryID) {
  615.                 /* found it! */
  616.             PrevMTp = MTp;  /* prepare for next item */
  617.             break;
  618.             }
  619.             if (MTp == PrevMTp) {
  620.                 /* scan completed, item not found! */
  621.                 MTp = &MenuStaticBind[0]; /* use default */
  622.                 break;
  623.             }
  624.             if (MTp->m_word == 0) {
  625.                     MTp = &MenuStaticBind[0];   /* cycle through */
  626.                 }
  627.                 else ++MTp;
  628.         }
  629.     }
  630.  
  631.     /*-for each item found in the binding table, display a key binding */
  632.     if (MTp != &MenuStaticBind[0]) UpdateMenuItemText (hMenu, Position, MTp);
  633.  
  634.     /*-if not quiescent, gray most items */
  635.         if (notquiescent) {
  636.             switch (EntryID) {
  637. #ifdef  IDM_DEBUG
  638.         case IDM_DEBUG:
  639. #endif
  640.         case IDM_QUIT:
  641.         case IDM_CTRLG:
  642.         case IDM_ABOUT:
  643.         case IDM_WHELPINDEX:
  644.         case IDM_WHELPKEYBOARD:
  645.         case IDM_WHELPCOMMANDS:
  646.         case IDM_WHELPPROCEDURES:
  647.         Enable = TRUE;
  648.         break;
  649.         default:
  650.         Enable = FALSE;
  651.         break;
  652.         }
  653.     }
  654.     else {
  655.         /*-if quiescent, gray the invalid items, depending on
  656.            appropriate conditions */
  657.         Enable = TRUE;
  658.             switch (EntryID) {
  659.         case IDM_FILESAVE:
  660.         case IDM_UNMARK:
  661.         /* grayed if current buffer is unmarked */
  662.         if (!(curbp->b_flag & BFCHG)) Enable = FALSE;
  663.         break;
  664.             case IDM_NEXTBUFFER:
  665.         case IDM_USEBUFFER:
  666.         case IDM_KILLBUFFER:
  667.         /* grayed if sole visible buffer */
  668.         if (getdefb () == NULL) Enable = FALSE;
  669.         break;
  670.         case IDM_NARROW:
  671.             /* grayed if already narrowed */
  672.         if (curbp->b_flag & BFNAROW) Enable = FALSE;
  673.             /* and if no mark... */
  674.             case IDM_CUTREGION:
  675.             case IDM_CLIPREGION:
  676.             case IDM_EDIT_REGION_POPUP:
  677.         /* grayed if no mark0 in current window */
  678.         if (curwp->w_markp[0] == NULL) Enable = FALSE;
  679.         break;
  680.             case IDM_REMMARK:
  681.             case IDM_SWAPMARK:
  682.             case IDM_GOTOMARK:
  683.                 /* grayed if no mark at all in current window */
  684.                 {
  685.                     int     i;
  686.                     Enable = FALSE;
  687.                     for (i = 0; i <= 9; i++) if (curwp->w_markp[i]) {
  688.                         Enable = TRUE;
  689.                         break;
  690.                     }
  691.                 }
  692.                 break;
  693.         case IDM_WIDEN:
  694.         /* grayed if not narrowed */
  695.         if (!(curbp->b_flag & BFNAROW)) Enable = FALSE;
  696.         break;
  697.         case IDM_DELWIND:
  698.         case IDM_ONLYWIND:
  699.         case IDM_NEXTWIND:
  700.         case IDM_PREVWIND:
  701.         case IDM_NEXTUP:
  702.         case IDM_NEXTDOWN:
  703.         case IDM_FILE_WINDOW_SIZE_POPUP:
  704.         /* grayed if only one window in current screen */
  705.         if (wheadp->w_wndp == NULL) Enable = FALSE;
  706.         break;
  707.         case IDM_EDIT_CLIPBOARD_POPUP:
  708.         /* grayed if clipboard empty and no mark set */
  709.         if ((curwp->w_markp[0] == NULL) &&
  710.             !IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
  711.         break;
  712.         case IDM_INSERTCLIP:
  713.         /* grayed if clipboard empty */
  714.         if (!IsClipboardFormatAvailable (CF_TEXT)) Enable = FALSE;
  715.         break;
  716.         case IDM_YANK:
  717.         /* grayed if no kill buffer */
  718.         if (kbufh == NULL) Enable = FALSE;
  719.         break;
  720.         case IDM_FORWHUNT:
  721.         case IDM_BACKHUNT:
  722.         /* grayed if no search string */
  723.         if (pat[0] == '\0') Enable = FALSE;
  724.         break;
  725.         case IDM_CTLXE:
  726.         /* grayed if no keyboard macro */
  727.         break;
  728.         }
  729.  
  730.         /*-If view mode, gray the items flagged IDM_NOTVIEW */
  731.         if (curbp->b_mode & MDVIEW) {
  732.             if (!(EntryID & (IDM_DYNAMIC | IDM_POPUP)) &&
  733.                     (MTp->m_word & IDM_NOTVIEW)) Enable = FALSE;
  734.         }
  735.         }
  736.  
  737.         EnableMenuItem (hMenu, Position,
  738.                         MF_BYPOSITION | (Enable ? MF_ENABLED : MF_GRAYED));
  739.     }
  740. } /* InitMenuPopup */
  741.  
  742. /* SimulateExtendedKey:    feed an extended key into the input stream */
  743. /* ===================                                                */
  744.  
  745. static void pascal near SimulateExtendedKey (int ec)
  746. {
  747.     char    prefix;
  748.  
  749.     if (in_room(5)) {
  750.     prefix = ec >> 8;
  751.     if (prefix != 0) {
  752.         in_put (0);
  753.         in_put (prefix);
  754.         if (prefix & (MOUS >> 8)) {
  755.         /* in case the key is a mouse action, supply
  756.            dummy mouse position info */
  757.         in_put (1);
  758.         in_put (1);
  759.         }
  760.     }
  761.     in_put (ec & 0xFF);
  762.     }
  763. } /* SimulateExtendedKey */
  764.  
  765. /* MenuCommand: WM_COMMAND message handling */
  766. /* ===========                              */
  767.  
  768. /* returns TRUE if the command has been recognized and FALSE otherwise
  769.    */
  770.  
  771. BOOL far pascal MenuCommand (WORD wParam, DWORD lParam)
  772. {
  773.     FARPROC     ProcInstance;
  774.     DWORD       HelpContext;
  775.  
  776.     switch (wParam) {   /* the menu choices from here down to the
  777.                default statement are valid even in the
  778.                not-quiescent case */
  779.     case IDM_ABOUT:
  780.     ProcInstance = MakeProcInstance ((FARPROC)AboutDlgProc,
  781.                      hEmacsInstance);
  782.     DialogBox (hEmacsInstance, "ABOUT", hFrameWnd, ProcInstance);
  783.     FreeProcInstance (ProcInstance);
  784.     break;
  785.     
  786. #ifdef  IDM_DEBUG
  787.     case IDM_DEBUG:     /* places a call to the debugger */
  788.     DebugBreak ();
  789.     break;
  790. #endif
  791.     
  792.     case IDM_QUIT:
  793.     PostMessage (hFrameWnd, WM_CLOSE, 0, 0L);
  794.     break;
  795.     
  796.     case IDM_CTRLG:
  797.     /* this is a special case: we feed the abort key into the input
  798.        stream */
  799.     SimulateExtendedKey (abortc);
  800.     break;
  801.     
  802.     case IDM_WHELPINDEX:
  803.     HelpContext = 0;
  804.     goto InvokeHelp;
  805.     case IDM_WHELPKEYBOARD:
  806.     HelpContext = HELPID_KEYBOARD;
  807.     goto InvokeHelp;
  808.     case IDM_WHELPCOMMANDS:
  809.     HelpContext = HELPID_COMMANDS;
  810.     goto InvokeHelp;
  811.     case IDM_WHELPPROCEDURES:
  812.     HelpContext = HELPID_PROCEDURES;
  813. InvokeHelp:
  814.     WinHelp (hFrameWnd, MainHelpFile,
  815.                  HelpContext ? HELP_CONTEXT : HELP_INDEX, HelpContext);
  816.     MainHelpUsed = TRUE;
  817.     break;
  818.     
  819.     default:
  820.     if (notquiescent) return TRUE;  /* abort processing */
  821.  
  822.     /* the following IDs are processed only if emacs is quiescent */
  823.     switch (wParam) {
  824.  
  825.     case IDM_GLOBMODE:
  826.     case IDM_MODE:
  827.         ProcInstance = MakeProcInstance ((FARPROC)ModeDlgProc,
  828.                          hEmacsInstance);
  829.         DialogBoxParam (hEmacsInstance, "MODES",
  830.                 hFrameWnd, ProcInstance,
  831.                 (DWORD)(wParam == IDM_GLOBMODE));
  832.         FreeProcInstance (ProcInstance);
  833.         if (wParam = IDM_MODE) {
  834.         upmode ();
  835.         update (FALSE);
  836.         }
  837.         break;
  838.  
  839.     case IDM_FONT:
  840.         PickEmacsFont ();
  841.         break;
  842.  
  843.     case IDM_CASCADE:
  844.     case IDM_TILE:
  845.     case IDM_ARRANGEICONS:
  846.         {
  847.         WORD    MDIMsg;
  848.  
  849.         switch (wParam) {
  850.         case IDM_CASCADE:
  851.             MDIMsg = WM_MDICASCADE;
  852.             break;
  853.         case IDM_TILE:
  854.             MDIMsg = WM_MDITILE;
  855.             break;
  856.         case IDM_ARRANGEICONS:
  857.             MDIMsg = WM_MDIICONARRANGE;
  858.             break;
  859.         }
  860.         SendMessage (hMDIClientWnd, MDIMsg, 0, 0L);
  861.         }
  862.         break;
  863.  
  864.     case IDM_NORMALIZE:
  865.         {
  866.         SCREEN  *sp;
  867.  
  868.         sp = (SCREEN*)GetWindowLong
  869.              (LOWORD(SendMessage (hMDIClientWnd,
  870.                       WM_MDIGETACTIVE, 0, 0L)),
  871.               GWL_SCRPTR);
  872.         newsize (TRUE, sp->s_nrow);
  873.         newwidth (TRUE, sp->s_ncol);
  874.         update (FALSE);
  875.         }
  876.         break;
  877.         
  878.     default:
  879.         if (wParam >= IDM_FIRSTCHILD) return FALSE;
  880.         GenerateMenuSeq (wParam);
  881.         return TRUE;
  882.     }
  883.     }
  884.     
  885.     /* we have processed an internal menu command */
  886.     if (!notquiescent) GenerateMenuSeq (IDM_NULLPROC);
  887.         /* this flushes a possible numeric argument */
  888.     return TRUE;
  889. } /* MenuCommand */
  890.  
  891. /* GetScreenMenuHandle: returns the handle to the 'Screen' menu (for MDI mgt) */
  892. /* ===================                                                        */
  893.  
  894. HMENU far pascal GetScreenMenuHandle (void)
  895. {
  896.     HMENU   hBaseMenu;
  897.     int     Pos;
  898.  
  899.     hBaseMenu = GetMenu (hFrameWnd);
  900.     for (Pos = GetMenuItemCount (hBaseMenu) - 1; Pos >= 0; Pos--) {
  901.         if (GetMenuEntryID (hBaseMenu, Pos) == IDM_SCREEN_POPUP) {
  902.             return GetSubMenu (hBaseMenu, Pos);
  903.         }
  904.     }
  905.     return NULL;
  906. } /* GetScreenMenuHandle */
  907.  
  908. /* MenuEntryCount:  count of menu entries, excluding the MDI buttons */
  909. /* ==============                                                    */
  910.  
  911. static int pascal near  MenuEntryCount (HMENU hMenu)
  912. {
  913.     int     Count;
  914.  
  915.     Count = GetMenuItemCount (hMenu);
  916.     if (hMenu == GetMenu (hFrameWnd)) {
  917.         if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
  918.             /* an MDI child is maximized ==> we have MDI buttons in the
  919.            menu bar */
  920.         Count -= 2;
  921.     }
  922.     }
  923.     return Count;
  924. } /* MenuEntryCount */
  925.  
  926. /* MenuEntryOffset:  position of the first non-MDI button menu entry */
  927. /* ===============                                                   */
  928.  
  929. static int pascal near  MenuEntryOffset (HMENU hMenu)
  930. {
  931.     if (hMenu == GetMenu (hFrameWnd)) {
  932.         if (HIWORD(SendMessage (hMDIClientWnd, WM_MDIGETACTIVE, 0, 0L))) {
  933.             /* an MDI child is maximized ==> we have MDI buttons in the
  934.            menu bar */
  935.         return 1;
  936.     }
  937.     }
  938.     return 0;
  939.     
  940. } /* MenuEntryOffset */
  941.  
  942. /* ParseMenu:   parse a piece of menu path */
  943. /* =========                               */
  944.  
  945. static BOOL pascal near ParseMenu (char *Name, char *Title, int *Posp)
  946.  
  947. /* Puts the text part of the menu name in Title (at most MAXMENUTITLE
  948.    characters including the \0) and the position number in *Posp. If no
  949.    position is specified, *Posp is set to -1. The returned BOOL is TRUE
  950.    if no error occured and FAILED if a syntax problem was diagnosed */
  951. {
  952.     register int i;
  953.  
  954.     *Posp = -1;
  955.     for (i = 0; Name[i] != '\0'; i++) {
  956.         if (i >= MAXMENUTITLE - 1) {
  957.             mlwrite (TEXT300);  /* "[Incorrect menu]" */
  958.             return FAILED;
  959.         }
  960.         if (Name[i] == '>') break;
  961.         if (Name[i] == '@') {   /* must parse a position index */
  962.             unsigned char *s;
  963.  
  964.             *Posp = 0;
  965.             for (s = &Name[i+1]; (*s != '>') && (*s != '\0'); s++) {
  966.                 if ((*s >= '0') && (*s <= '9')) {
  967.                     *Posp = (*Posp * 10) + (*s - '0');
  968.                 }
  969.                 else {      /* non numerical character! */
  970.                     mlwrite (TEXT300);  /* "[Incorrect menu]" */
  971.                     return FAILED;
  972.                 }
  973.             }
  974.             break;
  975.         }
  976.         Title[i] = Name[i];
  977.     }
  978.     Title[i] = '\0';
  979.     return TRUE;
  980. } /* ParseMenu */
  981.  
  982. /* LocateMenu:  locate a menu entry matching a piece of menu path */
  983. /* ==========                                                     */
  984.  
  985. static BOOL pascal near LocateMenu (char *Name, CURMENU *CM)
  986.  
  987. /* The returned BOOL is TRUE if a matching menu entry was found, FALSE
  988.    if no such entry was found and FAILED if a syntax problem was
  989.    diagnosed. The CURMENU structure is updated if the entry was found.
  990.    */
  991. {
  992.     HMENU   hMenu;
  993.     int     ItemCount;
  994.     int     Pos, StartPos;
  995.     BOOL    Result, DoScan;
  996.     char    Target[MAXMENUTITLE];
  997.     char    EntryName[MAXMENUTITLE];
  998.  
  999.     while (*Name == '<') {
  1000.     ++Name;
  1001.     --CM->cm_x;
  1002.     CM->cm_pos = -1;
  1003.     }
  1004.     if ((Result = ParseMenu (Name, Target, &Pos)) != TRUE) {
  1005.     return Result;
  1006.     }
  1007.     if (CM->cm_x < 0) {
  1008.     hMenu = GetMenu (hFrameWnd);
  1009.     }
  1010.     else hMenu = CM->cm_parent[CM->cm_x];
  1011.     ItemCount = GetMenuItemCount (hMenu);
  1012.     if (DoScan = (Pos < 0)) Pos = CM->cm_pos;
  1013.     if (Pos < 0) Pos = 0;
  1014.     StartPos = Pos;
  1015.     do {
  1016.     char    *s;
  1017.  
  1018.     GetMenuString (hMenu, Pos, (LPSTR)&EntryName[0],
  1019.                MAXMENUTITLE, MF_BYPOSITION);
  1020.     if ((s = strchr (EntryName, '\t')) != NULL) *s = '\0';
  1021.         /* discard the accelerator/key-binding info */
  1022.     if (strcmp (EntryName, Target) == 0) goto MenuLocated;
  1023.         if (!DoScan) {
  1024.             if (*Target == '\0') goto MenuLocated;
  1025.             else break; /* no matching target at indicated position */
  1026.         }
  1027.     if (++Pos >= ItemCount) Pos = 0;
  1028.     } while (Pos != StartPos);
  1029.     return FALSE;   /* not found */
  1030. MenuLocated:
  1031.     CM->cm_pos = Pos - MenuEntryOffset (hMenu);
  1032.     return TRUE;
  1033. } /* LocateMenu */
  1034.  
  1035. /* AddMenuEntry:    add a menu entry (or, recursively, a cascade of entries) */
  1036. /* ============                                                              */
  1037.  
  1038. static BOOL pascal near AddMenuEntry (char *Name, WORD ID, CURMENU *CM,
  1039.                                       WORD *MenuType)
  1040. /* the MenuType is a set of flags set returned by this function:
  1041.    MT_DUMMY indicates that the menu item is a mere separator, MT_MENUBAR
  1042.    indicates that the menu bar has been modified */
  1043. {
  1044.     HMENU   hMenu, hPopup;
  1045.     int     Pos;
  1046.     BOOL    Result;
  1047.     char    EntryName[MAXMENUTITLE];
  1048.     WORD    MenuFlags;
  1049.  
  1050.     if ((Result = ParseMenu (Name, EntryName, &Pos)) != TRUE) {
  1051.         return Result;
  1052.     }
  1053.     if (CM->cm_x < 0) {
  1054.     hMenu = GetMenu (hFrameWnd);
  1055.     *MenuType |= MT_MENUBAR;
  1056.     }
  1057.     else hMenu = CM->cm_parent[CM->cm_x];
  1058.     Name = strchr(Name, '>');
  1059.     if (Name != NULL) {     /* our task is to set up a popup menu */
  1060.     if (CM->cm_x >= MAXMENUPARENT - 1) {
  1061.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1062.         return FALSE;
  1063.     }
  1064.     if ((hPopup = CreatePopupMenu ()) == NULL) {
  1065.         mlwrite (TEXT302);  /* "[lack of resources]" */
  1066.         return ABORT;
  1067.     }
  1068.     MenuFlags = MF_POPUP | MF_STRING;
  1069.     }
  1070.     else {  /* this is a menu entry, not a popup */
  1071.     if (strcmp (EntryName, "-") == 0) {    /* only a separator */
  1072.         MenuFlags = MF_SEPARATOR;
  1073.         *MenuType |= MT_DUMMY;
  1074.         ID = 0;     /* contrary to SDK documentation, InsertMenu
  1075.                does not ignore the ID for a separator! */
  1076.     }
  1077.     else {
  1078.         MenuFlags = MF_STRING;
  1079.     }
  1080.     }
  1081.     if (Pos < 0) {
  1082.         Pos = CM->cm_pos + 1;       /* to insert after current entry */
  1083.     if (Pos <= 0) Pos = MenuEntryCount (hMenu); /* to append at end */
  1084.     }
  1085.     Result = InsertMenu (hMenu, Pos + MenuEntryOffset (hMenu),
  1086.                          MenuFlags | MF_BYPOSITION,
  1087.                          Name ? hPopup : ID,
  1088.                          (LPSTR)&EntryName[0]);
  1089.     if (!Result) {
  1090.         mlwrite (TEXT302);  /* "[lack of resources]" */
  1091.         Result = FALSE;
  1092.     }
  1093.     else Result = TRUE;
  1094.     if (Name == NULL) {
  1095.         CM->cm_pos = Pos;
  1096.     }
  1097.     else {
  1098.         if (Result != TRUE) {
  1099.             DestroyMenu (hPopup);
  1100.         }
  1101.         else {
  1102.         CM->cm_parent[++CM->cm_x] = hPopup;
  1103.         CM->cm_pos = -1;
  1104.         ++Name;     /* skip the '>' */
  1105.         if ((Result = AddMenuEntry (Name, ID, CM, MenuType)) != TRUE) {
  1106.         /* Menu creation failed at some point, we must undo our own */
  1107.         DeleteMenu (hMenu, Pos + MenuEntryOffset (hMenu),
  1108.                             MF_BYPOSITION);
  1109.         }
  1110.     }
  1111.     }
  1112.     return Result;
  1113. } /* AddMenuEntry */
  1114.  
  1115. /* AddMenuBinding:  bind a menu item to an EPOINTER */
  1116. /* ==============                                   */
  1117.  
  1118. static BOOL pascal near AddMenuBinding (ETYPE EPOINTER eptr, WORD type)
  1119.  
  1120. /* called by bindtomenu and macrotomenu, once the function or macro name
  1121.    has been parsed */
  1122. {
  1123.     BOOL    Result;
  1124.     WORD    MenuType = 0;   /* info returned by AddMenuEntry */
  1125.     char    FullName[NSTRING];
  1126.     char    *Name;
  1127.     WORD    DynIndex;
  1128.     CURMENU CM;
  1129.  
  1130.     /*-Let's make sure we have a free entry in the binding table */
  1131.     for (DynIndex = 0; MenuDynBind[DynIndex].m_word != 0; DynIndex++) {
  1132.         if (DynIndex >= MAXDYNMENU) {
  1133.             mlwrite (TEXT17);
  1134.             /* "Binding table FULL!" */
  1135.             return FALSE;
  1136.         }
  1137.     }
  1138.  
  1139.     /*-Parse the menu Name */
  1140.     Name = &FullName[0];
  1141.     if ((Result = mlreply (TEXT306, Name, NSTRING)) != TRUE) {
  1142.             /* "Menu: " */
  1143.         return Result;
  1144.     }
  1145.     if (*Name == '>') {
  1146.         CM.cm_pos = -1;
  1147.         CM.cm_x = -1;
  1148.         ++Name;
  1149.     }
  1150.     else CM = CurrentMenu;
  1151.     while ((Result = LocateMenu (Name, &CM)) == TRUE) {
  1152.         /* ...looping until we hit a piece of the menu path that does
  1153.        not yet exist */
  1154.     HMENU   hMenu;
  1155.     
  1156.     if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
  1157.     else hMenu = CM.cm_parent[CM.cm_x];
  1158.     hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
  1159.     if (hMenu == NULL) {
  1160.         /* this is not a popup menu, we loose! */
  1161.         mlwrite (TEXT300);  /* "[Incorrect menu]" */
  1162.         return FALSE;
  1163.     }
  1164.     if (CM.cm_x >= MAXMENUPARENT - 1) {
  1165.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1166.         return FALSE;
  1167.     }
  1168.     CM.cm_parent[++CM.cm_x] = hMenu;
  1169.     CM.cm_pos = -1;
  1170.     Name = strchr (Name, '>');
  1171.     if (Name == NULL) {
  1172.         /* premature end of menu path! */
  1173.         mlwrite (TEXT300);  /* "[Incorrect menu]" */
  1174.         return FALSE;
  1175.     }
  1176.     ++Name;     /* skip the '>' */
  1177.     }
  1178.     if (Result != FALSE) return Result;  /* syntax error, no cigar! */
  1179.  
  1180.     /*-Create the menu entry (or cascade of entries) */
  1181.     if ((Result = AddMenuEntry (Name, DynIndex + IDM_DYNAMIC, &CM,
  1182.                                 &MenuType)) != TRUE) {
  1183.         return Result;
  1184.     }
  1185.     CurrentMenu = CM;
  1186.  
  1187.     if (!(MenuType & MT_DUMMY)) {
  1188.     /*-complete the binding */
  1189.     MenuDynBind[DynIndex].m_word = type;
  1190.     MenuDynBind[DynIndex].m_ptr = eptr;
  1191.     }
  1192.     
  1193.     if (MenuType & MT_MENUBAR) DrawMenuBar (hFrameWnd);
  1194.     return TRUE;
  1195. } /* AddMenuBinding */
  1196.  
  1197. /* bindtomenu:  bind a menu item to an emacs function */
  1198. /* ==========                                         */
  1199.  
  1200. PASCAL bindtomenu (int f, int n)
  1201. /* command arguments IGNORED */
  1202. {
  1203.     ETYPE EPOINTER  e;
  1204.  
  1205.     e.fp = getname (TEXT304);   /* "Function: " */
  1206.     if (e.fp == NULL) {
  1207.         mlwrite (TEXT16);       /* "[No such function]" */
  1208.         return FALSE;
  1209.     }
  1210.     return AddMenuBinding (e, MB_FNC);
  1211. } /* bindtomenu */
  1212.  
  1213. /* macrotomenu: bind a menu item to a macro (i.e. a buffer) */
  1214. /* ===========                                              */
  1215.  
  1216. PASCAL macrotomenu (int f, int n)
  1217. /* command arguments IGNORED */
  1218. {
  1219.     ETYPE EPOINTER  e;
  1220.     char    Name[NBUFN];
  1221.     BOOL    Result;
  1222.  
  1223.     if ((Result = mlreply(TEXT305, &Name[1], NBUFN-2)) != TRUE) {
  1224.             /* "Macro: " */
  1225.         return Result;
  1226.     }
  1227.     Name[0] = '[';
  1228.     strcat (Name, "]");
  1229.     if ((e.buf = bfind (Name, FALSE, 0)) == NULL) {
  1230.         mlwrite (TEXT130);
  1231.         /* "Macro not defined" */
  1232.         return FALSE;
  1233.     }
  1234.     return AddMenuBinding (e, MB_BUF);
  1235. } /* macrotomenu */
  1236.  
  1237. /* DeleteMenuBinding:   remove a menu entry and its binding or sub-entries */
  1238. /* =================                                                       */
  1239.  
  1240. static BOOL pascal near DeleteMenuBinding (HMENU hMenu, int Pos)
  1241. /* returns TRUE except when an attempt is made to delete the 'Screen'
  1242.    menu */
  1243. {
  1244.     BOOL    Result;
  1245.     WORD    ID;
  1246.     HMENU   hPopup;
  1247.  
  1248.     Pos += MenuEntryOffset (hMenu);
  1249.     if ((ID = GetMenuItemID (hMenu, Pos)) != -1) {
  1250.         /* this is a pure menu item */
  1251.         if (ID >= IDM_DYNAMIC) {
  1252.             /* let's free the dynamic binding table entry */
  1253.             MenuDynBind[ID - IDM_DYNAMIC].m_word = 0;
  1254.         }
  1255.     }
  1256.     else if ((hPopup = GetSubMenu (hMenu, Pos)) != NULL) {
  1257.         /* this is a popup, we must delete all the contained entries */
  1258.         int     i;
  1259.  
  1260.         if (ID == IDM_SCREEN_POPUP) {
  1261.             /* this menu is used by the system's MDI manager to list the
  1262.            MDI childs. We cannot remove it */
  1263.             return FALSE;
  1264.         }
  1265.         for (i = MenuEntryCount (hPopup); i > 0;) {
  1266.             DeleteMenuBinding (hPopup, --i);
  1267.         }
  1268.     }   /* else, we assume it is a mere separator */
  1269.     DeleteMenu (hMenu, Pos, MF_BYPOSITION);
  1270.     if (hMenu == GetMenu (hFrameWnd)) DrawMenuBar (hFrameWnd);
  1271.     return TRUE;
  1272. } /* DeleteMenuBinding */
  1273.  
  1274. /* unbindmenu:  remove a menu entry */
  1275. /* ==========                       */
  1276.  
  1277. PASCAL unbindmenu (int f, int n)
  1278. /* command arguments IGNORED */
  1279. {
  1280.     BOOL    Result;
  1281.     char    FullName[NSTRING];
  1282.     char    *Name;
  1283.     CURMENU CM;
  1284.  
  1285.     Name = &FullName[0];
  1286.     if ((Result = mlreply (TEXT303, Name, NSTRING)) != TRUE) {
  1287.             /* "Menu: " */
  1288.         return Result;
  1289.     }
  1290.     if (*Name == '>') {
  1291.         CM.cm_pos = -1;
  1292.         CM.cm_x = -1;
  1293.         ++Name;
  1294.     }
  1295.     else CM = CurrentMenu;
  1296.     while ((Result = LocateMenu (Name, &CM)) == TRUE) {
  1297.     HMENU   hMenu;
  1298.     
  1299.     if (CM.cm_x < 0) hMenu = GetMenu (hFrameWnd);
  1300.     else hMenu = CM.cm_parent[CM.cm_x];
  1301.     Name = strchr (Name, '>');
  1302.     if (Name != NULL) {     /* the menu path continues beyond this... */
  1303.         hMenu = GetSubMenu (hMenu, CM.cm_pos + MenuEntryOffset (hMenu));
  1304.         if (hMenu == NULL) {
  1305.             /* this is not a popup menu, we loose! */
  1306.             Result = FALSE;
  1307.             break;
  1308.         }
  1309.         if (CM.cm_x >= MAXMENUPARENT - 1) {
  1310.         mlwrite (TEXT301);  /* "[Too many nested popup menus]" */
  1311.         return FALSE;
  1312.         }
  1313.         CM.cm_parent[++CM.cm_x] = hMenu;
  1314.         CM.cm_pos = 0;
  1315.         ++Name;     /* skip the '>' */
  1316.         /* and loop once again... */
  1317.     }
  1318.     else {                  /* this is it: the end of the menu path */
  1319.         int     Pos;
  1320.  
  1321.         if ((Result = DeleteMenuBinding (hMenu, CM.cm_pos)) != TRUE) {
  1322.             return FALSE;
  1323.         }
  1324.         if ((Pos = MenuEntryCount (hMenu) - 1) < CM.cm_pos) {
  1325.         /* we just deleted the item at the menu's end. This
  1326.            position is no longer valid */
  1327.         CM.cm_pos = Pos;
  1328.         }
  1329.         CurrentMenu = CM;
  1330.         break;      /* we are done */
  1331.     }
  1332.     }
  1333.     /* we arrive here if the menu was not found or a syntax error occured */
  1334.     if (Result == FALSE) mlwrite (TEXT300); /* "[Incorrect menu]" */
  1335.     return Result;
  1336. } /* unbindmenu */
  1337.